﻿/**
* CRL
*/
using CRL.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace CRL.Data
{
    public class ModelCheck
    {
        #region 检查表
        /// <summary>
        /// 检查索引
        /// </summary>
        /// <param name="type"></param>
        /// <param name="db"></param>
        /// <param name="removeId"></param>
        public static void CheckIndexExists(Type type, AbsDBExtend db, bool ignorePrimaryKey = false)
        {
            var list = GetIndexScript(type, null, db, ignorePrimaryKey);
            foreach (var item in list)
            {
                if (string.IsNullOrEmpty(item))
                {
                    continue;
                }
                var message = item;
                try
                {
                    db.Execute(item);
                    message += " 成功";
                }
                catch (Exception ero)//出错,
                {
                    message += $" 失败 {ero.Message}";
                    //EventLog.Log(string.Format("创建索引失败:{0}\r\n{1}", ero.Message, item));
                }
                Console.WriteLine(message);
                EventLog.Log(message);
            }
        }

        internal static Tuple<string, List<string>> CreateColumn(AbsDBExtend db, Attribute.FieldInnerAttribute item, bool exec)
        {
            var dbAdapter = db._DBAdapter;
            string str = dbAdapter.GetCreateColumnScript(db.dbContext, item);
            //string indexScript = "";
            //if (item.FieldIndexType != Attribute.FieldIndexType.无)
            //{
            //    indexScript = dbAdapter.GetColumnIndexScript(db.dbContext, item);
            //}
            var indexScripts = GetIndexScript(item.ModelType, new List<Attribute.FieldInnerAttribute> { item }, db, false);
            if (exec)
            {
                string result;
                try
                {
                    db.Execute(str);
                    if (indexScripts.Any())
                    {
                        foreach (var indexScript in indexScripts)
                        {

                            db.Execute(indexScript);
                        }
                    }

                    result = string.Format("创建字段:{0} {1} {2}\r\n", item.ModelType.Name, item.MemberName, item.PropertyType);
                    var model = System.Activator.CreateInstance(item.ModelType) as IModel;

                    try
                    {
                        model?.OnColumnCreated(item.MemberName);
                    }
                    catch (Exception ero)
                    {
                        result = string.Format("添加字段:{0} {1},升级数据时发生错误:{2}\r\n", item.ModelType.Name, item.MemberName, ero.Message);
                    }
                }
                catch (Exception ero)
                {
                    //EventLog.Log("创建字段时发生错误:" + ero.Message);
                    result = string.Format("创建字段:{0} {1}发生错误:{2}\r\n", item.ModelType.Name, item.MemberName, ero.Message);
                }
                EventLog.Log(result, "");
                Console.WriteLine(result);
            }
            return new Tuple<string, List<string>>(str, indexScripts);
        }
        internal static List<Attribute.FieldInnerAttribute> CheckNotExistsColumns(Type type, Dictionary<string, string> dbFields)
        {
            var allFileds = TypeCache.GetTable(type).FieldsDic;
            var list = new List<Attribute.FieldInnerAttribute>();
            if (!dbFields.Any())
            {
                return list;
            }
            foreach (var kv in allFileds)
            {
                if (!dbFields.ContainsKey(kv.Value.MapingName))//数据库中不存在
                {
                    list.Add(kv.Value);
                }
            }
            return list;
        }

        internal static List<string> GetIndexScript(Type type, List<Attribute.FieldInnerAttribute> columns, AbsDBExtend db, bool ignorePrimaryKey)
        {
            var dbAdapter = db._DBAdapter;
            List<string> list2 = new List<string>();
            if (columns == null)
            {
                columns = TypeCache.GetTable(type).Fields;
            }

            var table = TypeCache.GetTable(type);
            var fields = columns.Select(b => b.MemberName);
            var fields2 = table.Index.Where(b => fields.Contains(b.Key));
            foreach (var item in fields2)
            {
                var field = table.Fields.Find(b => b.MemberName == item.Key);
                if (field == null)
                {
                    continue;
                }
                list2.Add(dbAdapter.GetCreateIndexScript("", table, field.FieldIndexType == Attribute.FieldIndexType.非聚集唯一, $"IX_{field.MapingName}", dbAdapter.KeyWordFormat(field.MapingName)));
            }
            foreach (var item in table.UnionIndex)
            {
                var indexName = item.Key;
                list2.Add(dbAdapter.GetCreateIndexScript("", table, item.Value.FieldIndexType == Attribute.FieldIndexType.非聚集唯一, indexName, item.Value.Fields.Select(b => dbAdapter.KeyWordFormat(b)).ToArray()));
            }
            return list2;
        }

        /// <summary>
        /// 创建表
        /// </summary>
        /// <param name="type"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        public static string CreateTable(Type type, AbsDBExtend db)
        {
            string msg;
            CreateTable(type, db, out msg);
            return msg;
        }

        internal static Dictionary<string, string> GetDbFields(AbsDBExtend db, string tableName)
        {
            var dbAdapter = db._DBAdapter;
            //string tableName = TypeCache.GetTableName(type, db.dbContext);
            var sql = dbAdapter.GetTableFields(tableName);
            var dbFileds = db.ExecDictionary<string, string>(sql);
            return new Dictionary<string, string>(dbFileds, StringComparer.OrdinalIgnoreCase);
        }
        public static bool CreateTable(Type type, AbsDBExtend db, out string message)
        {
            return CreateTable(type, db, "", out message);
        }
        /// <summary>
        /// 创建表
        /// 会检查表是否存在,如果存在则检查字段
        /// 创建失败则抛出异常
        /// 表存在返回失败
        /// </summary>
        /// <param name="type"></param>
        /// <param name="db"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public static bool CreateTable(Type type, AbsDBExtend db, string tableName, out string message)
        {
            var dbAdapter = db._DBAdapter;
            message = "";
            var table = TypeCache.GetTable(type);
            var oldTableName = table.TableName;
            if (string.IsNullOrEmpty(tableName))
            {
                tableName = TypeCache.GetTableName(type, db.dbContext);
            }
            else
            {
                table.TableName = tableName;
            }
            var dbFileds = GetDbFields(db, tableName);
            bool needCreate = !dbFileds.Any();
            if (needCreate)
            {
                try
                {
                    var columns = TypeCache.GetTable(type).Fields;
                    dbAdapter.CreateTable(db.dbContext, columns, tableName);
                    message = string.Format("创建表:{0}\r\n", tableName);
                    CheckIndexExists(type, db);
                    //return true;
                }
                catch (Exception ero)
                {
                    message = "创建表时发生错误 类型{0} {1}\r\n";
                    message = string.Format(message, type, ero.Message);
                    table.TableName = oldTableName;
                    throw new Exception(message);
                    //return false;
                }
                //EventLog.Log(message, "", false);
            }
            else
            {
                var notExists = CheckNotExistsColumns(type, dbFileds);
                foreach (var f in notExists)
                {
                    CreateColumn(db, f, true);
                }
            }
            //EventLog.Log(message);
            if (!string.IsNullOrEmpty(message))
            {
                Console.WriteLine(message);
            }
            table.TableName = oldTableName;
            return true;
        }

        /// <summary>
        /// 同步表字段结构
        /// 会删除数据库中多余的字段
        /// </summary>
        /// <param name="type"></param>
        /// <param name="db"></param>
        public static void SyncTableFields(Type type, AbsDBExtend db, bool exec, out List<string> updateScripts)
        {
            var dbAdapter = db._DBAdapter;
            var helper = db.__DbHelper;
            updateScripts = new List<string>();
            if (helper.CurrentDBType == DBAccess.DBType.NPGSQL)
            {
                //取到的类型值不一致
                return;
            }
            var table = TypeCache.GetTable(type);
            var tableName = TypeCache.GetTableName(type, db.dbContext);

            var dbFileds = GetDbFields(db, tableName);
            var allFileds = table.FieldsDic.ToDictionary(b => b.Value.MapingName.ToLower(), b => b.Value);
            //var mapping = dbAdapter.GetFieldMaping();
            foreach (var kv in dbFileds)
            {
                if (string.IsNullOrEmpty(kv.Value))
                {
                    continue;
                }
                var a = allFileds.TryGetValue(kv.Key.ToLower(), out var f);
                if (!a)
                {
                    //删除数据库中
                    var script = dbAdapter.GetDropColumnScript(tableName, dbAdapter.KeyWordFormat(kv.Key));
                    updateScripts.Add(script);
                }
                else
                {
                    //if(f.ValueNeedConvert)
                    //{
                    //    continue;
                    //}
                    if (f.IsPrimaryKey)
                    {
                        continue;
                    }
                    var columnType = dbAdapter.GetColumnType(f, out var defaultValue);
                    if (!string.IsNullOrEmpty(f.ColumnType))
                    {
                        columnType = f.ColumnType;
                    }
                    if (string.IsNullOrEmpty(columnType))
                    {
                        continue;
                    }
                    columnType = columnType.Trim();
                    //去除长度
                    columnType = Regex.Replace(columnType, @"\(.+?\)", "");
                    var dbType = kv.Value;
                    dbType = Regex.Replace(dbType, @"\(.+?\)", "");
                    if (columnType.ToLower() != dbType.ToLower())
                    {
                        //更新
                        var script = dbAdapter.GetUpdateColumnScript(tableName, f).Split(';');
                        updateScripts.AddRange(script);
                    }
                }
            }
            //新增
            var notExists = CheckNotExistsColumns(type, dbFileds);
            foreach (var f in notExists)
            {
                var tuple = CreateColumn(db, f, false);
                updateScripts.Add(tuple.Item1);
                updateScripts.AddRange(tuple.Item2);
            }
            if (!exec)
            {
                return;
            }
            foreach (var s in updateScripts)
            {
                db.Execute(s);
                Console.WriteLine($"SyncTableFields: {s}");
            }
            //索引
            var index2 = GetIndexScript(type, null, db, true);
            foreach (var s in index2)
            {
                try
                {
                    db.Execute(s);
                    Console.WriteLine($"SyncTableFields: {s}");
                }
                catch { }
            }
            return;
        }

        public static void UpdateTableFiledComments(Type type, AbsDBExtend db)
        {
            var dbAdapter = db._DBAdapter;
            var helper = db.__DbHelper;
            var all = SummaryAnalysis.GetFieldComments(type);
            var table = all.Find(b => b.Key == type).Value;
            var updates = table.Fields.FindAll(b => !string.IsNullOrEmpty(b.Remark));
            dbAdapter.UpdateTableComment(helper, table.TableName, table.Remark);
            Console.WriteLine($"SyncTableComment: {table.TableName} {table.Remark}");
            foreach (var f in updates)
            {
                dbAdapter.UpdateFieldComment(helper, table.TableName, f, f.Remark);
                Console.WriteLine($"SyncTableFieldComment: {f} {f.Remark}");
            }
        }
        #endregion
    }
}
